/*
 * 实现客户端的请求发送逻辑
 */
#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>
#include <functional>

namespace jsonrpc
{
    namespace client
    {
        class Requestor
        {
        public:
            using ptr = std::shared_ptr<Requestor>;
            using RequestCallback = std::function<void(const BaseMessage::ptr &)>; // 请求回调
            using AsyncResponse = std::future<BaseMessage::ptr>;                   // 异步响应
            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;
                BaseMessage::ptr request;
                RType rtype;
                std::promise<BaseMessage::ptr> response;
                RequestCallback callback;
            };
            void onResponse(const BaseConnection::ptr &conn, const BaseMessage::ptr &msg)
            {
                std::string rid = msg->getRid();
                auto rdp = getDescribe(rid);
                if (rdp.get() == nullptr)
                {
                    LOG_ERROR("onResponse: can't find request describe, rid: %s", rid.c_str());
                    return;
                }
                if (rdp->rtype == RType::REQ_CALLBACK)
                {
                    if (rdp->callback)
                        rdp->callback(msg);
                }
                else if (rdp->rtype == RType::REQ_ASYNC)
                {
                    rdp->response.set_value(msg);
                }
                else
                {
                    LOG_ERROR("onResponse: unknown request type");
                }
                eraseDescribe(rid);
            }
            // 异步发送
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
            {
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
                if (rdp.get() == nullptr)
                {
                    LOG_ERROR("send: new request describe failed");
                    return false;
                }
                conn->send(req);
                async_rsp = rdp->response.get_future();
                return true;
            }
            // 同步发送
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
            {
                AsyncResponse rsp_future;
                bool ret = send(conn, req, rsp_future);
                if (ret == false)
                {
                    return false;
                }
                rsp = rsp_future.get();
                return true;
            }
            // 带回调的发送
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, const RequestCallback &cb)
            {
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK, cb);
                if (rdp.get() == nullptr)
                {
                    LOG_ERROR("send: new request describe failed");
                    return false;
                }
                conn->send(req);
                return true;
            }

        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr &request, RType rtype, const RequestCallback &cb = RequestCallback())
            {
                std::unique_lock<std::mutex> lock(mtx);
                RequestDescribe::ptr rd = std::make_shared<RequestDescribe>();
                rd->request = request;
                rd->rtype = rtype;
                if (rtype == RType::REQ_CALLBACK && cb)
                {
                    rd->callback = cb;
                }
                request_map[request->getRid()] = rd;
                return rd;
            }
            RequestDescribe::ptr getDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(mtx);
                auto it = request_map.find(id);
                if (it != request_map.end())
                {
                    return it->second;
                }
                return RequestDescribe::ptr();
            }
            void eraseDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(mtx);
                request_map.erase(id);
            }

        private:
            std::mutex mtx;
            std::unordered_map<std::string, RequestDescribe::ptr> request_map;
        };
    }
}